#include <iostream>
#include <utility>
#include <crypto/securedatamapitem.h>

#include "utils/functions.h"
#include <QDebug>
#define DELIMETER "@#!"
namespace crypto {
SecureDataMapItem::SecureDataMapItem() : _isEmpty(true)
{

}

SecureDataMapItem::SecureDataMapItem(const std::string &service_name, const std::string &login,
                                     const Botan::secure_vector<uint8_t> &password,
                                     const Botan::secure_vector<uint8_t> &notes,
                                     const std::weak_ptr<cipherManager> &manager)
	: _service_name(QString::fromStdString(service_name)), _login(QString::fromStdString(login)),
	_password(password), _notes(notes), _manager(manager),
	_isEmpty(false)
{

}

SecureDataMapItem::SecureDataMapItem(QString service_name, QString login,
                                     const Botan::secure_vector<uint8_t> &password,
                                     const Botan::secure_vector<uint8_t> &notes,
                                     const std::weak_ptr<cipherManager> &manager)
	: _service_name(std::move(service_name)), _login(std::move(login)),
	_password(password), _notes(notes), _manager(manager),
	_isEmpty(false)
{

}


SecureDataMapItem::SecureDataMapItem(const std::string &data, std::weak_ptr<cipherManager> manager):_manager(std::move(manager)) {
	auto splitedData = utils::split(data, DELIMETER);
	if(4 != splitedData.size()) {
		std::cerr << "There is not enough data" << std::endl;
		_isEmpty = true;
	} else {
		_service_name = QString::fromStdString(splitedData[0]);
		_login = QString::fromStdString(splitedData[1]);
		_password = Botan::secure_vector<uint8_t>(splitedData[2].begin(), splitedData[2].end());
		_notes = Botan::secure_vector<uint8_t>(splitedData[3].begin(), splitedData[3].end());
		_isEmpty = false;
	}
}

SecureDataMapItem::SecureDataMapItem(const SecureDataMapItem &item) {
	this->_service_name = item._service_name;
	this->_login = item._login;
	this->_password = item._password;
	this->_notes = item._notes;
	this->_manager = item._manager;
	this->_isEmpty = item._isEmpty;
}

QString SecureDataMapItem::getDecryptedData(const utils::DataType type) const {
	switch(type) {
		case utils::SERVICE_NAME:
			return _service_name;
		case utils::LOGIN:
			return _login;
		case utils::PASSWORD: {
			if(auto ciphManager = _manager.lock()) {
				auto decryptedText = ciphManager->decrypt(_password,false);
				QString pass = QString::fromUtf8(reinterpret_cast<const char*>(decryptedText.data()), decryptedText.size());
				qDebug() << QString("Pass: ") << pass;
				return pass;
			} else {
				std::cerr << "Error manager was destroyed!" << std::endl;
			}
		}
		case utils::NOTES:{
			if(auto ciphManager = _manager.lock()) {
				auto decryptedText = ciphManager->decrypt(_notes,false);
				QString notes = QString::fromUtf8(reinterpret_cast<const char*>(decryptedText.data()), decryptedText.size());
				return notes;
			} else {
				std::cerr << "Error manager was destroyed!" << std::endl;
			}
		}
		break;
		default:
			return QString{};
	}
	return QString{};
}

std::string SecureDataMapItem::getEncryptedData(const utils::DataType type) const {
	switch(type) {
		case utils::SERVICE_NAME:
			return _service_name.toStdString();
		case utils::LOGIN:
			return _login.toStdString();
		case utils::PASSWORD: {
			std::string utf8_text = std::string(reinterpret_cast<const char*>(_password.data()), _password.size());
			return utf8_text;
		}
		case utils::NOTES:{
			std::string utf8_text = std::string(reinterpret_cast<const char*>(_notes.data()), _notes.size());
			return utf8_text;
		}
		default:
			return std::string{};
	}
}

void SecureDataMapItem::setDecryptedData(const utils::DataType type, const QString& el) {
	switch(type) {
		case utils::SERVICE_NAME:
			_service_name = el;
		break;
		case utils::LOGIN:
			_login = el;
		break;
		case utils::PASSWORD: {
			if(auto ciphManager = _manager.lock()) {
				auto tempStr = el.toStdString();
				auto password = Botan::secure_vector<uint8_t>(tempStr.begin(), tempStr.end());
				_password = ciphManager->encrypt(password, false);
			} else {
				std::cerr << "Error manager was destroyed!" << std::endl;
			}
		}
		break;
		case utils::NOTES:{
			if(auto ciphManager = _manager.lock()) {
				auto tempStr = el.toStdString();
				auto notes = Botan::secure_vector<uint8_t>(tempStr.begin(), tempStr.end());
				_notes = ciphManager->encrypt(notes, false);
			} else {
				std::cerr << "Error manager was destroyed!" << std::endl;
			}
		}
		break;
		default:
			break;
	}
}

void SecureDataMapItem::setEncryptedData(const utils::DataType type, const Botan::secure_vector<uint8_t> &el) {
	switch(type) {
		case utils::SERVICE_NAME:
			_service_name = QString::fromUtf8(reinterpret_cast<const char*>(el.data()), el.size());
		break;
		case utils::LOGIN:
			_login = QString::fromUtf8(reinterpret_cast<const char*>(el.data()), el.size());
		break;
		case utils::PASSWORD: {
			_password = el;
		}
		break;
		case utils::NOTES:{
			_notes = el;
		}
		break;
		default:
			break;
	}
}

void SecureDataMapItem::setManager(const std::weak_ptr<cipherManager> &manager) {
	_manager = manager;
}

void SecureDataMapItem::clear()
{
	_service_name.fill(QChar('\0'));
	_login.fill(QChar('\0'));
	std::fill(_password.begin(), _password.end(), 0);
	std::fill(_notes.begin(), _notes.end(), 0);
	_service_name.clear();
	_login.clear();
	_password.clear();
	_notes.clear();
}

void SecureDataMapItem::reDecryptData(const Botan::InitializationVector& newIv) {
	if(auto ciphManager = _manager.lock()) {
		_password = ciphManager->updateDecryption(newIv, _password);
		_notes = ciphManager->updateDecryption(newIv, _notes);
	}
}

bool SecureDataMapItem::empty() const {
	return _isEmpty;
}


std::string to_string(const SecureDataMapItem &item) {
	auto pass = item.getEncryptedData(utils::DataType::PASSWORD);
	std::string res = item.getEncryptedData(utils::DataType::SERVICE_NAME) + DELIMETER
					+ item.getEncryptedData(utils::DataType::LOGIN) + DELIMETER
					+ item.getEncryptedData(utils::DataType::PASSWORD) + DELIMETER
					+ item.getEncryptedData(utils::DataType::NOTES) + DELIMETER;
	return res;
}
}